#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>

#import "DXFParser.h"

#ifndef PI
#define PI 3.1415926585
#endif

#import "AutocadColors.h"

static void strip( char *s ) {
	int len = strlen(s);
	int i;
	for (i=0; i<=len; i++) {
		if (s[i] == '\n' || s[i] == '\r') s[i] = '\0';
	}
}

int getLines( char *s1, char *s2, int n, FILE *inputFile ) {
	
	if (fgets( s1, n, inputFile ) == NULL) return 0;
	if (fgets( s2, n, inputFile ) == NULL) return 0;
	strip(s1);
	strip(s2);
	return 1;
		
}


void countGroupCode( NSMutableDictionary *dictionary, NSString *code ) {

	NSDictionary *data = nil;
	int count = 1;
	if ( data = [dictionary objectForKey: code]) {
		count = [[ data objectForKey:@"Count"] intValue];
		count++;
	}
	
	data = [ NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: count], @"Count", code, @"Code", NULL];
	
	[ dictionary setObject: data forKey: code ];
	
	
}

NSInteger sortByOccurrences(id obj1, id obj2, void *arg3) {
	
	NSDictionary *dict1 = (NSDictionary *)obj1;
	NSDictionary *dict2 = (NSDictionary *)obj2;
	
	int count1 = [[ dict1 objectForKey:@"Count" ] intValue];
	int count2 = [[ dict2 objectForKey:@"Count" ] intValue];

	return count2 - count1;

}


typedef enum { LINE=1, CIRCLE, VERTEX, ARC, POLYLINE, TEXT } entity_environment;
typedef enum { HEADER=1, CLASSES, TABLES, BLOCKS, ENTITIES, OBJECTS, INSERT } section_environment;

CGPoint convertToOCS( CGPoint p, DXFNode *node ) {

	if ( [ node childExistsForCode: 210] ) {		
		double z = [[node childOfCode: 230] doubleValue];
		if ( z < 0 ) {
			return CGPointMake( -p.x, p.y);
		}
	}
	return p;
	
}

void drawNode( DXFParser *parser, DXFNode *node, FILE *output, CGAffineTransform t, NSMutableDictionary *drawParameters ) {
	
	int r = 0;
	int g = 0;
	int b = 0;

	BOOL byLayer = NO;
	BOOL byBlock = NO;
	
	if ( [node childExistsForCode: 67 ] ) {
		if ( [[node childOfCode: 67] intValue] == 1 ) {
			// NSLog(@"Node is in paper space! %@", [node description]);
			return;
		}
	}
	
	
	if ( [node childExistsForCode: 60 ] ) {
		if ( [[node childOfCode: 60] intValue] == 0 ) {
			// object marked not visible
		//	return;
		}
	}
	
	if ( [node childExistsForCode: 62 ] ) {
		int index = [[ node childOfCode: 62] intValue];
		if ( index == 0 ) {
			byBlock = YES;
		}
		else if ( index == 256 ) {
			byLayer = YES;
		}
		else {
					
			[ AutocadColors getRed: &r green: &g blue: &b forIndex: index ];
		}
	}
	else {
		byLayer = YES;
	}
	
	
	if (byLayer && [node childExistsForCode: 8 ]) {
		NSString *layerName = [[node childOfCode: 8] stringValue];
		DXFNode *layer = [ parser getLayer: layerName ];
		if ( layer ) {
			if ( [layer childExistsForCode: 62] ) {
				int layerColorIndex = [[ layer childOfCode: 62] intValue];
				if ( layerColorIndex < 0 ) {
					// layer is OFF
					return;
				}
				else {
				//	NSLog(@"index = %d", layerColorIndex);
					[ AutocadColors getRed: &r green: &g blue: &b forIndex: layerColorIndex ];	
				}
				
			}
		}
	}
	
	
	
	NSString *style = [ NSString stringWithFormat: @"stroke:rgb(%d,%d,%d); stroke-width:1; fill: none", r, g, b];;
	
	if ( node.groupCode == 0 ) {
	
		if ([node.stringValue isEqual:@"LINE"]) {
			CGPoint a, b;
			a.x = [[ node childOfCode: 10 ] doubleValue];
			b.x = [[ node childOfCode: 11 ] doubleValue];
			a.y = [[ node childOfCode: 20 ] doubleValue];
			b.y = [[ node childOfCode: 21 ] doubleValue];
			fprintf( output, "<line style=\"%s\" x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\"/>\n", [style UTF8String], a.x, a.y, b.x, b.y );
		}
		else if ([node.stringValue isEqual:@"ARC"]) {	
			
			CGPoint center;
			center.x = [[ node childOfCode: 10 ] doubleValue];
			center.y = [[ node childOfCode: 20 ] doubleValue];
			
			double radius  = [[ node childOfCode: 40 ] doubleValue];
			double startAngle = [[ node childOfCode: 50 ] doubleValue];
			double endAngle = [[ node childOfCode: 51 ] doubleValue];
			CGPoint p1, p2;
			p1.x = center.x + radius * cos( startAngle * PI / 180.0 );
			p1.y = center.y + radius * sin( startAngle * PI / 180.0 );
			p2.x = center.x + radius * cos( endAngle * PI / 180.0 );
			p2.y = center.y + radius * sin( endAngle * PI / 180.0 );
			
			BOOL dir1 = ( p1.x - center.x ) * ( p2.y - center.y ) - ( p2.x - center.x ) * ( p1.y - center.y ) < 0;
						
			center = convertToOCS( center, node   );
			p1 = convertToOCS( p1, node );
			p2 = convertToOCS( p2, node );
			
			BOOL dir2 = ( p1.x - center.x ) * ( p2.y - center.y ) - ( p2.x - center.x ) * ( p1.y - center.y ) < 0;
			
			BOOL largeArc = dir1;
			
			BOOL sweep = ( dir2 == dir1 );
			
			fprintf( output, "<path style=\"%s\" d=\"M%f,%f A%f,%f 0 %d,%d %f,%f\"/>\n", [style UTF8String], p1.x, p1.y, radius, radius, largeArc, sweep, p2.x, p2.y  );
		}
		else if ([node.stringValue isEqual:@"ELLIPSE"]) {
			CGPoint p;
			p.x			= [[ node childOfCode: 10 ] doubleValue];
			p.y			= [[ node childOfCode: 20 ] doubleValue];
			
		}
		else if ([node.stringValue isEqual:@"CIRCLE"]) {
			CGPoint p;
			p.x			= [[ node childOfCode: 10 ] doubleValue];
			p.y			= [[ node childOfCode: 20 ] doubleValue];
			p = convertToOCS( p, node );
			double r	= [[ node childOfCode: 40 ] doubleValue];
			fprintf( output, "<circle style=\"%s\" cx=\"%f\" cy=\"%f\" r=\"%f\"/>\n", [style UTF8String], p.x, p.y, r  );
		}
		else if ([node.stringValue isEqual:@"SOLID"]) {		

			CGPoint p1, p2, p3, p4;
			p1.x = [[ node childOfCode: 10 ] doubleValue];
			p2.x = [[ node childOfCode: 11 ] doubleValue];
			p3.x = [[ node childOfCode: 12 ] doubleValue];
			p4.x = [[ node childOfCode: 13 ] doubleValue];
			p1.y = [[ node childOfCode: 20 ] doubleValue];
			p2.y = [[ node childOfCode: 21 ] doubleValue];
			p3.y = [[ node childOfCode: 22 ] doubleValue];
			p4.y = [[ node childOfCode: 23 ] doubleValue];
			
			fprintf(output, "<polyline style=\"stroke:rgb(0,0,0); stroke-width:2; fill: black\" points=\"%f,%f %f,%f %f,%f %f,%f %f,%f\"/>\n",
					p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, p1.x, p1.y );

		}
		else if ([node.stringValue isEqual:@"TEXT"] || [node.stringValue isEqual:@"ATTRIB"]) {		
						
			CGPoint p;
			p.x			= [[ node childOfCode: 10 ] doubleValue];
			p.y			= [[ node childOfCode: 20 ] doubleValue];
			p			= convertToOCS(p, node);
			
			NSString *s = [[ node childOfCode: 1 ] stringValue];
			
			float textHeight = [[ node childOfCode: 40 ] doubleValue];
			
			float rotation = 0.0;
			if ( [node childExistsForCode: 50]) {
				// note that both svg and autocad use degrees here
				rotation = [[ node childOfCode: 50] doubleValue];
			}
			
			fprintf(output, "<g transform=\"translate(%f, %f)\" font-size=\"%f\">\n", p.x, p.y, textHeight);
			
			fprintf(output, "<text style=\"%s\" transform=\"rotate(%f) scale(1,-1)\">%s</text>\n",
					[style UTF8String], rotation, [s UTF8String] );
			
			fprintf(output, "</g>\n");
			

		}
		else if ([node.stringValue isEqual:@"POLYLINE"]) {		
			
			BOOL closed = NO;
			if ( [node childExistsForCode: 70] ) {
				if ( [[node childOfCode: 70] intValue] & 1 ) {
					closed = YES;
				}
			}
			
			NSMutableArray *segments = [NSMutableArray arrayWithCapacity: 0];
			
			for ( DXFNode *lineChild in node.children ) {
				if ( lineChild.groupCode == 0 && [lineChild.stringValue isEqual:@"VERTEX"] ) {
					[segments addObject: lineChild];
				}
			}			
			
			fprintf(output, "<polyline style=\"%s\" points=\"", [style UTF8String]);
			int i;
			for ( i=0; i<[segments count]; i++ ) {
				DXFNode *s = [segments objectAtIndex: i];
				CGPoint p;
				p.x = [[s childOfCode: 10] doubleValue];
				p.y = [[s childOfCode: 20] doubleValue];
				p = convertToOCS( p, node );
				fprintf(output, "%f,%f ", p.x, p.y);
			}
			if ( closed && [segments count] > 2) {
				DXFNode *s = [segments objectAtIndex: 0];
				CGPoint p;
				p.x = [[s childOfCode: 10] doubleValue];
				p.y = [[s childOfCode: 20] doubleValue];
				p = convertToOCS( p, node );
				fprintf(output, "%f,%f ", p.x, p.y);				
			}
			fprintf(output, "\"/>\n");

			
		}		
		else if ([node.stringValue isEqual:@"INSERT"]) {

			
			NSString *blockName = [[ node childOfCode: 2 ] stringValue ];
			DXFNode *block = [ parser getBlock: blockName ];
			if ( block ) {
			
				CGAffineTransform t2 = CGAffineTransformIdentity;
								
				
				if ( [ node childExistsForCode: 41 ] ) {
					t2.a  = [[ node childOfCode: 41 ] doubleValue ];
					NSLog(@"x scale = %f", t2.a);
				}
				if ( [ node childExistsForCode: 42 ] ) {
					t2.d  = [[ node childOfCode: 42 ] doubleValue ];
					NSLog(@"y scale = %f", t2.d);
				}
				if ( [ node childExistsForCode: 50 ] ) {
					double angle = [[ node childOfCode: 50 ] doubleValue ];
					t2 = CGAffineTransformRotate(t2, angle * PI / 180);
				}
				if ( [ node childExistsForCode: 10 ] && [ node childExistsForCode: 20 ] ) {
					CGPoint insertionPoint;
					insertionPoint.x  = [[ node childOfCode: 10 ] doubleValue ];
					insertionPoint.y  = [[ node childOfCode: 20 ] doubleValue ];
					insertionPoint = convertToOCS( insertionPoint, node );
					t2.tx = insertionPoint.x;
					t2.ty = insertionPoint.y;
				}

				fprintf(output, "<g transform=\"matrix(%f,%f,%f,%f,%f,%f)\">\n", t2.a, t2.c, t2.b, t2.d, t2.tx, t2.ty );
				
				for ( DXFNode *blockChild in block.children ) {
					drawNode( parser, blockChild, output, CGAffineTransformIdentity, nil );
				}
				fprintf(output, "</g>\n");

			}
			
		}
		else if ([node.stringValue isEqual:@"SEQEND"] || 
				 [node.stringValue isEqual:@"DIMENSION"] ||
				 [node.stringValue isEqual:@"POINT"]) {
			;
		}
		else {
			NSLog(@"I don't know how to deal with %@", [node stringValue]);
		}
		
	}
	
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	if ( argc != 3 ) {
		NSLog(@"Usage %s [input.dxf] [output.svg]", argv[0]);
		return 1;
	}
	
	FILE *inputFile = fopen( argv[1], "r" );
	FILE *outputFile = fopen( argv[2], "w" );
	
	if (!inputFile) {
		NSLog(@"Could not open %s for reading", argv[1] );
		return 1;
	}
	if (!outputFile) {
		NSLog(@"Could not open %s for writing", argv[2] );
		return 1;
	}
	
	NSLog(@"Parsing");
	DXFParser *parser = [[DXFParser alloc] init];
	[parser parseFile: [NSString stringWithUTF8String: argv[1] ]];
	NSLog(@"Done");
	
	NSLog(@"paperspace viewport = %@", [[parser paperspaceViewport] description]);
	
	DXFNode *rootNode = [parser rootEntity];
	
	
	fprintf( outputFile, "<?xml version=\"1.0\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
	
	DXFNode *activeViewPort = nil;
	if ( activeViewPort = [parser getFirstActiveViewPort ] ) {
		
		double lowerLeftX = [[ activeViewPort childOfCode: 10 ] doubleValue];
		double lowerLeftY = [[ activeViewPort childOfCode: 20 ] doubleValue];
		double upperRightX = [[ activeViewPort childOfCode: 11 ] doubleValue];
		double upperRightY = [[ activeViewPort childOfCode: 21 ] doubleValue];

		double viewHeight = [[ activeViewPort childOfCode: 40 ] doubleValue];
		double aspectRatio = [[ activeViewPort childOfCode: 41 ] doubleValue];
		
		NSLog(@"(%f, %f, %f, %f)", lowerLeftX, lowerLeftY, upperRightX, upperRightY);
		NSLog(@"View height = %f, aspect ratio = %f", viewHeight, aspectRatio);
		
		fprintf( outputFile, "<svg width=\"%f\" height=\"%f\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", aspectRatio * viewHeight, viewHeight);
		fprintf( outputFile, "<g transform=\"translate(0, %f) scale(1,-1)\">\n", viewHeight);

		
	}
	
	for (DXFNode *node in rootNode.children ) {
		drawNode(parser, node, outputFile, CGAffineTransformIdentity, NULL );
	}
	fprintf( outputFile, "</g>\n" );
	fprintf( outputFile, "</svg>\n" );

    [pool drain];
	
	fclose(inputFile);
	fclose(outputFile);
	
	
	
    return 0;
}
